package com.desire.test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/*
    1.newSingleThreadExecutor
        介绍：创建一个单线程的线程池。这个线程池只有一个线程在工作，也就是相当于单线程串行执行所有任务。
        如果这个唯一的线程因为异常结束，那么会有一个新的线程来替代它。
        此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
        优点：单线程的线程池，保证线程的顺序执行
        缺点：不适合并发
    2.newFixedThreadPool
        介绍：创建固定大小的线程池。每次提交一个任务就创建一个线程，直到线程达到线程池的最大大小。
        线程池的大小一旦达到最大值就会保持不变，如果某个线程因为执行异常而结束，那么线程池会补充一个新线程。
        优点：固定大小线程池，超出的线程会在队列中等待
        缺点：不支持自定义拒绝策略，大小固定，难以扩展
    3.newCachedThreadPool
        介绍：创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程，那么就会回收部分
        空闲（60秒不执行任务）的线程，当任务数增加时，此线程池又可以智能的添加新线程来处理任务。
        此线程池不会对线程池大小做限制，线程池大小完全依赖于操作系统（或者说JVM）能够创建的最大线程大小。
        优点：很灵活，弹性的线程池线程管理，用多少线程给多大的线程池，不用后及时回收，用则新建
        缺点：一旦线程无限增长，会导致内存溢出
    4.newScheduledThreadPool
        介绍：创建一个定长线程池，支持定时及周期性任务执行
        优点：一个固定大小线程池，可以定时或周期性的执行任务
        缺点：任务是单线程方式执行，一旦一个任务失败其他任务也受影响
    总结
    1）以上线程池都不支持自定义拒绝策略。
    2）newFixedThreadPool 和 newSingleThreadExecutor：主要问题是堆积的请求处理队列可能会耗费非常大的内存，
        甚至 OOM。
    3）newCachedThreadPool 和 newScheduledThreadPool：主要问题是线程数最大数是 Integer.MAX_VALUE，
        可能会创建数量非常多的线程，甚至 OOM(Out Of Memory)。


 */


/**
 * @author LittleDesire
 * @description
 * @date 2022-02-09 周三 19:32
 * @since 17
 */
public class _54_线程池 {

    /**
     * 新的单线程执行器newSingleThreadExecutor
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        ExecutorService es = Executors.newCachedThreadPool();
        //建立固定大小的线程池
        //es = Executors.newFixedThreadPool(3);
        es = Executors.newSingleThreadExecutor();

        var t = new _54_线程池();
        //es.submit(t::work, "T1");
        //es.submit(t::work, "T2");
        //es.submit(t::work, "T3");
        //es.submit(t::work, "T4");

        es.execute(t::work);
        es.execute(t::work);
        es.execute(t::work);
        es.execute(t::work);

        System.out.println(es.isShutdown());

        // 关闭启动线程
        es.shutdown();
        // 等待子线程结束，再继续执行下面的代码
        boolean b = es.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        // true 所有子线程执行完毕
        System.out.println(b);
        System.out.println("all thread complete");

    }

    /**
     * 数组阻塞队列ArrayBlockingQueue
     *
     * @param args
     */
    public static void main4(String[] args) {
        //创建等待队列  capacity容量—此队列的容量
        BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<>(20);
        //创建线程池，池中保存的线程数为3，允许的最大线程数为5
        /*
            corePoolSize：线程池中所保存的核心线程数，包括空闲线程。
            maximumPoolSize：池中允许的最大线程数。
            keepAliveTime：线程池中的空闲线程所能持续的最长时间。
            unit：持续时间的单位。
            workQueue：任务执行前保存任务的队列，仅保存由execute方法提交的Runnable任务。

            根据ThreadPoolExecutor源码前面大段的注释，我们可以看出，当试图通过excute方法将一个Runnable任务添加到线程池中时，按照如下顺序来处理：
            1、如果线程池中的线程数量少于corePoolSize，即使线程池中有空闲线程，也会创建一个新的线程来执行新添加的任务；
            2、如果线程池中的线程数量大于等于corePoolSize，但缓冲队列workQueue未满，则将新添加的任务放到workQueue中，
               按照FIFO的原则依次等待执行（线程池中有线程空闲出来后依次将缓冲队列中的任务交付给空闲的线程执行）；
            3、如果线程池中的线程数量大于等于corePoolSize，且缓冲队列workQueue已满，但线程池中的线程数量小于maximumPoolSize，
               则会创建新的线程来处理被添加的任务；
            4、如果线程池中的线程数量等于了maximumPoolSize，有4种处理方式（该构造方法调用了含有5个参数的构造方法，并将最后一个构造方法
               为RejectedExecutionHandler类型，它在处理线程溢出时有4种方式，这里不再细说，要了解的，自己可以阅读下源码）。

                总结起来，也即是说，当有新的任务要处理时，先看线程池中的线程数量是否大于corePoolSize，再看缓冲队列workQueue是否满，
            最后看线程池中的线程数量是否大于maximumPoolSize。
                另外，当线程池中的线程数量大于corePoolSize时，如果里面有线程的空闲时间超过了keepAliveTime，就将其移除线程池，
            这样，可以动态地调整线程池中线程的数量。
         */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 50, TimeUnit.MILLISECONDS, bqueue);
        //创建七个任务
        Runnable t1 = new MyThread();
        Runnable t2 = new MyThread();
        Runnable t3 = new MyThread();
        Runnable t4 = new MyThread();
        Runnable t5 = new MyThread();
        Runnable t6 = new MyThread();
        Runnable t7 = new MyThread();
        //每个任务会在一个线程上执行
        pool.execute(t1);
        pool.execute(t2);
        pool.execute(t3);
        pool.execute(t4);
        pool.execute(t5);
        pool.execute(t6);
        pool.execute(t7);
        //关闭线程池
        pool.shutdown();
    }

    void work() {
        String tn = Thread.currentThread().getName();
        System.out.printf("%s:线程启动%n", tn);
        for (int i = 0; i < 2; i++) {
            System.out.printf("%s:%d%n", tn, i);
        }
        System.out.printf("%s:线程结束%n", tn);
    }
}

class MyThread implements Runnable {
    /**
     * newCachedThreadPool新的缓存线程池
     *
     * @param args
     */
    public static void main3(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<String>> resultList = new ArrayList<>();

        //创建10个任务并执行
        for (int i = 0; i < 10; i++) {
            //使用ExecutorService执行Callable类型的任务，并将结果保存在future变量中
            Future<String> future = executorService.submit(new TaskWithResult(i));
            //将任务执行结果存储到List中
            resultList.add(future);
        }

        //遍历任务的结果
        for (Future<String> fs : resultList) {
            try {
                while (!fs.isDone()) {
                    //Future返回如果没有完成，则一直循环等待，直到Future返回完成
                }
                System.out.println(fs.get());     //打印各个线程（任务）执行的结果
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            } finally {
                //启动一次顺序关闭，执行以前提交的任务，但不接受新任务
                executorService.shutdown();
            }
        }
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行。。。");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class TaskWithResult implements Callable<String> {
    private final int id;

    public TaskWithResult(int id) {
        this.id = id;
    }

    /**
     * newScheduledThreadPool新的计划线程池
     *
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main2(String[] args) throws ExecutionException, InterruptedException {
        // 1. 创建线程池  newScheduledThreadPool新的计划线程池
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        // 2. 分配任务
        // pool.shedule(Callalbe<T> callable, long delay, TimeUnit unit(时间单位))
        // 相当于开启了10个线程 并且放到线程池当中
        for (int i = 0; i < 10; i++) {
            Future<Integer> result = pool.schedule(new Callable<Integer>() {
                @Override
                public Integer call() {
                    // 产生100以内的随机数
                    int num = new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    return num;
                }
            }, 1, TimeUnit.SECONDS);
            // delay 延迟1s 之后每隔1s   Callable -要执行的函数 delay-从现在开始延迟执行的时间 unit-延迟参数的时间单位
        }
        //3. 关闭线程池
        pool.shutdown();

    }

    /**
     * newFixedThreadPool新的固定线程池
     *
     * @param args
     */
    public static void main1(String[] args) {
        //创建线程池  newFixedThreadPool新的固定线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        ThreadPoolDemo tpd = new ThreadPoolDemo();
        //为线程池分配任务
        for (int i = 0; i < 10; i++) {
            pool.submit(tpd);
        }
        //关闭线程池
        pool.shutdown();
    }

    /**
     * 任务的具体过程，一旦任务传给ExecutorService的submit方法，
     * 则该方法自动在一个线程上执行
     */
    @Override
    public String call() {
        System.out.println("call()方法被自动调用！！！    " + Thread.currentThread().getName());
        //该返回结果将被Future的get方法得到
        return "call()方法被自动调用，任务返回的结果是：" + id + "    " + Thread.currentThread().getName();
    }

    static class ThreadPoolDemo implements Runnable {

        private int i = 0;

        @Override
        public void run() {
            while (i <= 10) {
                System.out.println(Thread.currentThread().getName() + " : " + i++);
            }
        }
    }
}
